/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014-06-04
 * V4.0
 */
package com.jphenix.driver.propertie.instanceb;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.jphenix.driver.nodehandler.FNodeHandler;
import com.jphenix.kernel.classloader.ResourcesLoader;
import com.jphenix.kernel.objectloader.exception.BeanException;
import com.jphenix.kernel.objectloader.instanceb.BaseBean;
import com.jphenix.share.lang.SString;
import com.jphenix.share.printstream.PrintStreamTool;
import com.jphenix.share.util.BaseUtil;
import com.jphenix.share.util.DebugUtil;
import com.jphenix.share.util.SFilesUtil;
import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.propertie.IConfigSetter;
import com.jphenix.standard.viewhandler.INodeHandler;
import com.jphenix.standard.viewhandler.IViewHandler;

/**
 * XML格式的 配置文件处理类
 * 
 * getParameter 方法中的传入参数 key 
 * 
 * 支持 sub1/sub2/sub3/key 格式
 * 
 * 对应的xml格式为:
 * 
 *   <root>
 *   		<sub1>
 *   				<sub2>
 *   						<sub3>
 *   								<key>value</key>
 *   						</sub3>
 *   				</sub2>
 *   		</sub1>
 *   </root>
 * 
 * 配置文件中还支持引用外部文件，引用的文件作为该配置文件处理类的子类
 * 
 * 为什么引用的文件内容不能放在当前处理类中？因为涉及到信息保存，一个类对应一个配置文件。
 * <root>
 * 		<import id="子类主键">文件相对路径</import>
 * 
 *      <import>文件相对路径</import>  <!-- 子配置文件中的参数都归于当前配置文件对象中 -->
 * </root>
 * 
 * 注意： 引用外部文件标签只能用在文件的根节点内。
 * 比如： 上面的例子。
 * 
 * 不允许：
 * 
 *        <root>
 *        		<sub1>
 *        				<import id="子类主键">不允许这么写，必须写在根节点中</import>
 *        		</sub1>
 *        </root>
 * 
 * 17-03-28
 * 原本类中带保存配置文件的方法的，几乎从来没用过。
 * 增加了同一个类引用多个配置文件到当前类
 * 
 * 2018-11-08 解决了配置文件发生变化后，无法自动重新加载内容的问题。
 * 2019-01-24 重构了资源加载器，修改了加载器类路径
 * 2020-08-06 去掉了配置文件处理接口，改为实体类
 * 2021-09-12 完善了代码，避免了死循环错误
 * 
 * @author 刘虻
 * 2012-11-5 上午10:43:14
 */
@ClassInfo({"2021-09-12 18:45","XML格式的 配置文件处理类"})
public class XmlConfProp extends BaseBean {

	protected String                      propId             = null;                          //配置信息容器主键
	protected String                      originalFilePath   = null;                          //原始配置文件信息路径（相对路径）
	protected ArrayList<String>           childKeyList       = new ArrayList<>();             //子类主键序列
	protected HashMap<String,XmlConfProp> childMap           = new HashMap<>();               //子类容器
	protected HashMap<String,Object>    parameterMap         = new HashMap<>();               //参数容器
	protected IViewHandler              confXml              = FNodeHandler.newNodeHandler(); //配置文件核心类
	protected HashMap<String,Object>    globalParameterMap   = new HashMap<>();               //全局参数容器对象
	protected List<String>              confPathList         = new ArrayList<>();             //依次加载的配置文件路径
	protected Map<String,Long>          confFileTimeMap      = new HashMap<>();               //依次加载的配置文件最后更新时间对照容器 key：文件路径 value：最后更新时间
	protected List<IConfigSetter>       setterList           = new ArrayList<>();             //需要设置到配置信息类中数据的类实例序列
	
	/**
	 * 构造函数
	 * @author 刘虻
	 */
	public XmlConfProp() {
		super();
	}

	/**
	 * 覆盖方法
	 * 刘虻
	 * 2012-11-5 上午10:43:14
	 */
    public String getParameter(String key) {
		return fixSubPropMap(null,key);
	}
	
	
	/**
	 * 递归处理指定配置信息主键中的信息容器（确保不返回null）
	 * 刘虻
	 * 2012-11-5 下午4:59:01
	 * @param paraMap 对应的配置信息容器
	 * @param key 配置信息主键
	 * 
	 * 					主键格式：    sub1/sub2/key
	 * 
	 *                  容器：  HashMap  key:sub1  value:HashMap
	 *                                                              key:sub2   value:HashMap
	 *                                                                               key:key  value:value
	 * @return 指定的配置信息容器
	 */
	@SuppressWarnings("unchecked")
    protected String fixSubPropMap(HashMap<String,Object> paraMap,String key) {
		if(paraMap==null) {
			//通常首次访问时，paraMap都是空的，默认使用该类的信息包
			//递归调用时paraMap不为空，所以只检查一次
			checkModified(); //检测文件是否发生变化 
			paraMap = parameterMap;
		}
		int point = key.indexOf("/"); //参数分割点
		if(point>0) {
			String subKey = key.substring(0,point); //获取配置信息主键
			key = key.substring(point+1);  //处理剩下的主键信息
			
			if(childKeyList.contains(subKey)) {
				//获取子节点对象
				XmlConfProp child = (XmlConfProp)childMap.get(subKey);
				if(child==null) {
					return "";
				}
				return child.fixSubPropMap(null,key);
			}
			//获取配置信息主键对应的信息容器
			Object subMap = paraMap.get(subKey);
			if(subMap==null) {
				subMap = new HashMap<String,Object>();
				paraMap.put(subKey,subMap);
				return "";
			}
			if(subMap instanceof HashMap) {
				return fixSubPropMap((HashMap<String,Object>)subMap,key);
			}
			return "";
		}else {
			//在参数配置文件中，如果返回的值也带前缀，可以重新指向另外一个参数段
			//比如jar包内置的参数配置文件中，写固定的值，可以重定向到外部参数配置文件中指定值
			String keyVal = SString.valueOf(paraMap.get(key));
			if(keyVal.equals(key) || keyVal.equals("@"+key) || keyVal.startsWith("@"+key+"@")) {
				System.err.println("Error:The XmlConfProp The Key:["+key+"] Is Nesting Used");
				return "";
			}
			return getFixParameter(keyVal);
		}
	}


	/**
	 * 覆盖方法
     * pXml.getFirstChildNodeByNodeName("String").nt()	 * 刘虻
	 * 2012-11-5 上午10:43:14
	 */
    public XmlConfProp setFilePath(String configFilePath) throws Exception {
		if(configFilePath==null || configFilePath.length()<1) {
			return this;
		}
		clear(); //清空现有配置
		confPathList.clear();
		confPathList.add(configFilePath);
		parsePropertys(configFilePath); //解析配置文件
		return this;
	}
	
	
	/**
	 * 加入配置文件中的内容
	 * 注意：加入的配置文件如果发生变化，并不会重新加载
	 *      通常用于加载内部配置文件，因为内部配置文件时只读的
	 *      如果希望加载的配置文件发生变化，自动更新配置信息，
	 *      就用addChild加载子配置文件对象
	 * @param configFilePath 配置文件路径
	 * @return 当前类实例
	 * @throws Exception 异常
	 * 2017年3月23日
	 * @author MBG
	 */
    public XmlConfProp addFilePath(String configFilePath) throws Exception {
        //Xml配置文件
    	if(configFilePath==null || configFilePath.length()<1) {
    		return this;
    	}
    	confPathList.add(configFilePath);
		parsePropertys(configFilePath); //解析配置文件
		return this;
	}
	
	/**
	 * 清空已加载的配置内容
	 * @return
	 * 2018年11月8日
	 * @author MBG
	 */
	private void clear() {
		confXml = FNodeHandler.newNodeHandler();
		parameterMap.clear();
	}
	
	
	/**
	 * 判断配置文件是否发生变化
	 * @author 刘虻
	 * 2009-2-26下午01:39:00
	 */
	protected void checkModified() {
		boolean hasChanged = false;  //配置文件是否发生变化
		File    confFile;            //配置文件对象
		Long    time;                //最后更新时间
		for(String path:confPathList) {
			if(path.startsWith("!")) {
				continue;
			}
			time = confFileTimeMap.get(path);
			if(time==null) {
				continue;
			}
			try {
				confFile = SFilesUtil.getFileByName(path,null);
			}catch(Exception e) {
				e.printStackTrace();
				continue;
			}
			if(!confFile.exists()) {
				hasChanged = true;
				confPathList.remove(path);
				confFileTimeMap.remove(path);
			}
			if(time!=confFile.lastModified()) {
				hasChanged = true;
			}
		}
		if(hasChanged) {
			try {
				refreshParameter();
			}catch(Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	
	/**
	 * 递归设置参数信息
	 * 刘虻
	 * 2012-11-5 下午3:31:04
	 * @param xml 参数xml段
	 * @param pMap 参数容器
	 */
	protected void setPropInfo(IViewHandler xml,HashMap<String,Object> pMap) {
			if(xml==null) {
				return;
			}
			String nodeName = xml.nn(); //节点名作为参数名
			List<IViewHandler> cXmls = xml.getChildDealNodes(); //获取其子节点
			if(cXmls.size()>0) {
				//构建子信息容器
				HashMap<String,Object> cMap = new HashMap<String,Object>();
				pMap.put(nodeName,cMap);
				for(IViewHandler cXml:cXmls) {
					setPropInfo(cXml,cMap);
				}
			}else {
				String nodeValue = xml.nt(); //参数值
				if(nodeValue.length()>0) {
					pMap.put(nodeName,nodeValue);
				}
			}
	}

	
	/**
	 * 输入配置信息
	 * 刘虻
	 * 2012-11-6 上午9:55:07
	 * @param pst				输出流
	 * @param paraMap	配置信息容器
	 * @param tab				缩进字符串
	 */
	@SuppressWarnings("unchecked")
    protected void outParameterInfo(
	        PrintStreamTool pst,HashMap<String,Object> paraMap,String tab) {
		if(paraMap==null) {
			paraMap = parameterMap;
		}
		//获取参数主键序列
		List<String> keyList = BaseUtil.getMapKeyList(paraMap);
		Object value; //参数值
		for(String key:keyList) {
			value = paraMap.get(key);
			if(value==null) {
				continue;
			}
			if(value instanceof HashMap<?,?>) {
				pst.append(tab).append("<").append(key).append(">\r\n");
				outParameterInfo(pst,(HashMap<String,Object>)value,tab+"\t");
				pst.append(tab).append("</").append(key).append(">\r\n");
			}else {
				pst
					.append(tab)
					.append("<")
					.append(key)
					.append(">")
					.append(value)
					.append("</")
					.append("key")
					.append(">\r\n");
			}
		}
	}

	/**
	 * 覆盖方法
	 * 刘虻
	 * 2012-11-5 上午10:43:14
	 */
    public XmlConfProp refreshParameter() throws Exception {
		if(confPathList.size()<1) {
			//throw new BeanException("The Config File Path Is Null");
			return this;
		}
		clear(); //清除已存数据
		for(String path:confPathList) {
			parsePropertys(path); //解析配置文件
		}
		for(IConfigSetter setter:setterList) {
			parameterMap.putAll(setter.getPropertyMap());
		}
		return this;
	}
	
    
    /**
     * 解析配置文件
     * @param configXml   配置信息xml对象
     * @throws Exception  异常
     * 2021年9月12日
     * @author MBG
     */
    public void parsePropertys(IViewHandler configXml,String filePath) throws Exception {
    	if(configXml==null || configXml.isEmpty()) {
    		return;
    	}
    	confXml.addChildNode(configXml);
    	List<IViewHandler> cXmls = configXml.getChildDealNodes(); //获取参数段
    	String nodeId;
    	String nodeName; //节点名
    	String nodeValue; //节点值
    	String impFilePath; //引用配置文件的路径
    	IViewHandler cXml;
    	for(int i=0;i<cXmls.size();i++) {
    		cXml = cXmls.get(i);
    		nodeName = cXml.nn(); //获取节点名
    		/*
    		 * 处理引用节点
    		 */
    		if("import".equals(nodeName)) {
    			nodeValue = cXml.nt(); //获取节点值
    			nodeId = cXml.a("id"); //配置信息主键

    			if(nodeValue.startsWith("!")) {
    				//内部资源
    				if(_beanFactory!=null) {
    					//处理掉内部资源标识符
    					nodeValue = nodeValue.substring(1);
    					//内部资源加载器
    					ResourcesLoader rl = _beanFactory.getObject(ResourcesLoader.class,this);
    					if(nodeId.length()>0) {
    						//引用文件
    						XmlConfProp child = new XmlConfProp();
    						child.setPropId(nodeId); //配置信息主键
    						//文件相对路径 主要用来保存配置文件
    						//（保存配置文件中，不能使用全路径，否则影响迁移）
    						if(filePath!=null) {
    							child.originalFilePath = filePath; 
    						}
    						try {
    							child.parsePropertys(rl.getFile(nodeValue),nodeValue);
    						}catch(Exception e) {
    							e.printStackTrace();
    							throw new BeanException(
    									"Load Config File:["+nodeValue+"] Exception:"+DebugUtil.outException(e));
    						}
    						childKeyList.add(nodeId);
    						childMap.put(nodeId,child);
    					}else {
    						//将引用文件的内容放入当前层级配置信息中
    						try {
    							parsePropertys(rl.getFile(nodeValue),nodeValue);
    						}catch(Exception e) {
    							e.printStackTrace();
    							throw new BeanException(
    									"Load Config File:["+nodeValue+"] Exception:"+DebugUtil.outException(e));
    						}
    					}
    				}
    				continue;
    			}
    			if(filePath!=null) {
        			//处理相对路径
        			impFilePath = SFilesUtil.getAllFilePath(nodeValue,SFilesUtil.getFilePath(filePath,false));
        			if((new File(impFilePath)).exists()) {
        				System.out.println("**********************************\nBegin Load Child Property File:["+impFilePath+"]\n**********************************");
        				if(nodeId.length()>0) {
        					//引用文件
        					XmlConfProp child = new XmlConfProp();
        					child.setPropId(nodeId); //配置信息主键
        					//文件相对路径 主要用来保存配置文件
        					//（保存配置文件中，不能使用全路径，否则影响迁移）
        					child.originalFilePath = impFilePath; 
        					child.setFilePath(impFilePath); //文件路径

        					childKeyList.add(nodeId);
        					childMap.put(nodeId,child);
        				}else {
        					parsePropertys(impFilePath);
        				}
        			}else {
        				System.out.println("**********************************\nNot Find The Property File:["+impFilePath+"]\n**********************************");
        			}
    			}
    			continue;
    		}
    		//递归设置参数信息值
    		setPropInfo(cXml,parameterMap);
    	}
    }
	
	/**
	 * 解析配置文件
	 * @param is                读入流
	 * @throws Exception        异常
	 * 2014年4月19日
	 * @author 马宝刚
	 */
	public void parsePropertys(InputStream is,String filePath) throws Exception {
		if(is==null) {
			return;
		}
	    IViewHandler configXml = FNodeHandler.newNodeHandler("UTF-8"); //构建新的xml处理类
    	configXml.setXmlStyle(true);
    	try {
    		//Xml配置文件
    		((INodeHandler)configXml).setStream(is,filePath);
    		configXml = configXml.getFirstChildNodeByNodeName("root");
    	}catch(Exception e) {
    		e.printStackTrace();
    		throw new BeanException("Load Config File Exception Path:["+filePath+"]");
    	}
    	parsePropertys(configXml,filePath);
	}
	
	
	
	/**
	 * 解析配置文件
	 * @param filePath          配置文件全路径
	 * @throws Exception        异常
	 * 2014年4月19日
	 * @author 马宝刚
	 */
	public void parsePropertys(String filePath) throws Exception {
		File confFile; //配置文件对象
        try {
        	if(filePath.startsWith("!")) {
        		filePath = filePath.substring(1);
        		//内部资源
        		
        		String baseClassPath = SFilesUtil.getBaseClassPath(XmlConfProp.class);
        		baseClassPath = SFilesUtil.getAllFilePath("..",baseClassPath); //无论返回 /WEB-INF/lib  还是 /WEB-INF/classes
        		
        		//先判断外部是否有同路径同名文件，如果有，则取外部文件
        		confFile = SFilesUtil.getFileByName(filePath,baseClassPath);
        		if(confFile.exists()) {
            		confFileTimeMap.put(filePath,confFile.lastModified());
            		parsePropertys(new FileInputStream(confFile),filePath);
        		}else {
        			//内部资源加载器
        			ResourcesLoader rl = _beanFactory.getObject(ResourcesLoader.class,this);
                    try {
                    	parsePropertys(rl.getFile(filePath),filePath);
                    }catch(Exception e2) {
        				e2.printStackTrace();
        				System.err.println("Load Native Resource Config File:["+filePath+"] Exception:"+DebugUtil.outException(e2));
        			}  
        		}
        	}else {
        		confFile = SFilesUtil.getFileByName(filePath,null);
        		if(!confFile.exists()) {
        			confPathList.remove(filePath);
        			System.err.println("The Config File:["+filePath+"] Not Found");
        		}else {
            		confFileTimeMap.put(filePath,confFile.lastModified());
            		parsePropertys(new FileInputStream(confFile),filePath);
        		}
        	}
        }catch(Exception e) {
            e.printStackTrace();
            throw new BeanException("Load Config File Exception Path:["+filePath+"]");
        }
	}

	/**
	 * 覆盖方法
	 * 刘虻
	 * 2012-11-5 上午10:43:14
	 */
    public HashMap<String,Object> getParameterMap() {
		if(parameterMap==null) {
			parameterMap = new HashMap<String,Object>();
		}
		return parameterMap;
	}


	/**
	 * 获取整理后的参数值
	 * 
	 * 尝试检查parameter这个值是不是参数配置文件中的参数主键
	 * 如果是，从参数配置文件中获取对应的参数值
	 * 
	 * parameter值说明：
	 * 
	 * 开头为@的值是参数配置文件中的参数主键
	 * 除了开头@以外，parameter值中还有一个@的话
	 * 那么这个@后面的事默认值，即参数配置文件中
	 * 没有这个值，就返回默认值
	 * 
	 * 如果parameter开头不是@，那么这个parameter值
	 * 本身就是参数值（并不是参数配置文件中的主键）
	 * 
	 * @author 刘虻
	 * 2007-9-15下午04:09:43
	 * @param parameter 整理前的参数值
	 * @return 整理后的参数值
	 */
    public String getFixParameter(String parameter) {
		//整理参数
		if(parameter==null) {
			parameter = "";
		}
		if (parameter.indexOf("@")==0) {
			//去掉开头的标识
			parameter = parameter.substring(1);
			//再检查是否有默认值标识符
			//（除了开头的@，变量末尾还有一个@，在这个@后面是默认值）
			int point = parameter.indexOf("@");
			if(point>0) {
				//存在默认值
				String defValue = parameter.substring(point+1);
				parameter = parameter.substring(0,point);
				
				//尝试获取变量值
				String res = fixSubPropMap(null,parameter);
				if(res.length()<1) {
					res = defValue;
				}
				return res;
			}else {
				//从全局配置文件中获取值
				return fixSubPropMap(null,parameter);
			}
		}
		return parameter;
	}


	/**
	 * 设置配置信息主键
	 * 刘虻
	 * 2012-11-5 下午3:15:34
	 * @param propId 配置信息主键
	 */
	protected void setPropId(String propId) {
		this.propId = propId;
	}
	
	/**
	 * 获取配置信息主键
	 * 刘虻
	 * 2012-11-5 下午3:15:19
	 * @return 配置信息主键
	 */
	protected String getPropId() {
		return propId;
	}

	/**
	 * 覆盖方法
	 * 刘虻
	 * 2012-11-6 上午11:45:55
	 */
    public void addChild(String childID, XmlConfProp child) {
		childMap.put(childID,child);
		if(!childKeyList.contains(childID)) {
			childKeyList.add(childID);
		}
	}

	/**
	 * 覆盖方法
	 * 刘虻
	 * 2012-11-6 上午11:47:49
	 */
    public boolean hasParameter(String key) {
		return hasParameter(null,key);
	}
	
	
	/**
	 * 递归检测是否包含该主键的参数信息
	 * 刘虻
	 * 2012-11-6 上午11:49:52
	 * @param paraMap 参数信息容器
	 * @param key 参数主键
	 * @return true该参数存在
	 */
	@SuppressWarnings("unchecked")
    protected boolean hasParameter(HashMap<String,Object> paraMap,String key) {
		if(paraMap==null) {
			//通常首次访问时，paraMap都是空的，默认使用该类的信息包
			//递归调用时paraMap不为空，所以只检查一次
			checkModified(); //检测文件是否发生变化 
			paraMap = parameterMap;
		}
		int point = key.indexOf("/"); //参数分割点
		if(point>0) {
			String subKey = key.substring(0,point); //获取配置信息主键
			key = key.substring(point+1);  //处理剩下的主键信息
			
			if(childKeyList.contains(subKey)) {
				//获取子节点对象
				XmlConfProp child = (XmlConfProp)childMap.get(subKey);
				if(child==null) {
					return false;
				}
				return child.hasParameter(null,key);
			}
			//获取配置信息主键对应的信息容器
			HashMap<String,Object> subMap = (HashMap<String,Object>)paraMap.get(subKey);
			if(subMap==null) {
				return false;
			}
			return hasParameter(subMap,key);
		}else {
			return paraMap.containsKey(key);
		}
	}
	
	/**
	 * 获取配置文件中指定段的XML对象
	 * @param key 节点名
	 * @return 节点对象
	 * 2014年5月5日
	 * @author 马宝刚
	 */
    public IViewHandler getParameterXml(String key) {
	    checkModified(); //检测文件是否发生变化 
	    if(key==null || key.length()<1) {
	        return confXml;
	    }
	    //分割节点名
	    String[] keys = key.split("/");
	    IViewHandler fNode = confXml; //父节点
	    IViewHandler cNode = null;    //子节点
	    for(int i=0;i<keys.length;i++) {
	    	cNode = fNode.getFirstChildNodeByNodeName(keys[i]);
	    	if(cNode.isEmpty()) {
	    		cNode.setParentNode(fNode);
	    		return cNode;
	    	}
	    	fNode = cNode;
	    }
	    if(cNode==null) {
	    	cNode = confXml.n();
	    	cNode.setParentNode(confXml);
	    }
	    return cNode;
    }
    
    /**
     * 获取指定配置信息节点的指定属性值
     * @param key            节点名
     * @param attributeName  节点中的属性名
     * @return               对应的属性值
     * 2020年8月5日
     * @author MBG
     */
    public String getParamterAttribute(String key,String attributeName) {
    	return getParameterXml(key).getAttribute(attributeName);
    }
	
	/**
	 * 获取指定参数块
	 * 
	 * 配置文件中的参数块：
	 * 
	 * <参数块主键>
	 *     <参数1主键>参数1值</参数1主键>
	 *     <参数2主键>参数2值</参数2主键>
	 * </参数块主键>
	 * 
	 * @param key 参数主键
	 * @return 参数块容器
	 * 2014年4月19日
	 * @author 马宝刚
	 */
    public Map<String,Object> getParameterValueMap(String key) {
	    return getParameterValueMap(null,key);
	}
	
	/**
	 * 递归获取指定参数块
	 * 
     * 配置文件中的参数块：
     * 
     * <参数块主键>
     *     <参数1主键>参数1值</参数1主键>
     *     <参数2主键>参数2值</参数2主键>
     * </参数块主键>
     * 
	 * @param paraMap 参数容器或子参数容器
	 * @param key 参数主键
	 * @return 参数块容器
	 * 2014年4月19日
	 * @author 马宝刚
	 */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    protected Map<String,Object> getParameterValueMap(HashMap<String,Object> paraMap,String key) {
        if(paraMap==null) {
            //通常首次访问时，paraMap都是空的，默认使用该类的信息包
            //递归调用时paraMap不为空，所以只检查一次
            checkModified(); //检测文件是否发生变化 
            paraMap = parameterMap;
        }
	    if(key==null || key.length()<1) {
	        return (Map)paraMap.clone();
	    }
       int point = key.indexOf("/"); //参数分割点
        if(point>0) {
            String subKey = key.substring(0,point); //获取配置信息主键
            key = key.substring(point+1);  //处理剩下的主键信息
            if(childKeyList.contains(subKey)) {
                //获取子节点对象
                XmlConfProp child = (XmlConfProp)childMap.get(subKey);
                if(child==null) {
                    return new HashMap<String,Object>();
                }
                return child.getParameterValueMap(null,key);
            }
            //获取配置信息主键对应的信息容器
            HashMap<String,Object> subMap = (HashMap)paraMap.get(subKey);
            if(subMap==null) {
                return new HashMap<String,Object>();
            }
            return getParameterValueMap(subMap,key);
        }else {
            //获取配置信息主键对应的信息容器
            HashMap<String,Object> subMap = (HashMap)paraMap.get(key);
            if(subMap==null) {
                return new HashMap<String,Object>();
            }
            return (Map)subMap.clone();
        }
	}

    /**
     * 添加子类
     * @param child 子类实例
     * 2014年6月21日
     * @author 马宝刚
     */
    public void addChild(XmlConfProp child) {
        parameterMap.putAll(child.getParameterMap());
    }

    
    /**
     * 通过指定相对路径，获取对应的配置信息对象
     * 路径相对于当前配置文件的路径
     * @param path 相对于当前配置文件的路径
     * @return 指定配置文件对象
     * 2016年11月4日
     * @author MBG
     */
    public IViewHandler getConfigByPath(String path) {
    	//构建新的对象
    	INodeHandler reNh = FNodeHandler.newNodeHandler("UTF-8"); //构建新的xml处理类
    	reNh.setXmlStyle(true);
    	if(path==null || path.length()<1 || _beanFactory==null) {
    		return reNh;
    	}
    	try {
    		reNh.setFile(new File(SFilesUtil.getAllFilePath(path,_beanFactory.getWebInfPath())));
    	}catch(Exception e) {
    		e.printStackTrace();
    	}
    	return reNh;
    }

	/**
	 * 设置全局参数对象
	 * @param key 参数主键
	 * @param obj 参数值对象
	 * 2017年12月21日
	 * @author MBG
	 */
    public void setObject(String key, Object obj) {
		synchronized(globalParameterMap) {
			globalParameterMap.put(key,obj);
		}
	}

	/**
	 * 获取指定全局参数对象
	 * @param key 参数主键
	 * @return    全局参数值对象
	 * 2017年12月21日
	 * @author MBG
	 */
    public Object getObject(String key) {
		synchronized(globalParameterMap) {
			return globalParameterMap.get(key);
		}
	}
	
	/**
	 * 清空全局参数对象容器
	 * 2017年12月22日
	 * @author MBG
	 */
    public void clearObject() {
		globalParameterMap.clear();
	}
	
	/**
	 * 获取加载的配置文件路径序列
	 * @return 加载的配置文件路径序列
	 * 2018年11月8日
	 * @author MBG
	 */
    public List<String> getConfigPathList(){
		//构建返回值
		List<String> reList = new ArrayList<String>();
		for(String path:confPathList) {
			if(path==null || path.length()<1 || path.startsWith("!}")) {
				continue;
			}
			reList.add(path);
		}
		return reList;
	}
	
	/**
	 * 将需要设置到配置信息类中数据的类，注册到配置信息类中
	 * @param setter 需要设置到配置信息类中数据的类
	 * 2018年11月8日
	 * @author MBG
	 */
    public void registerSetter(IConfigSetter setter) {
		if(setter==null || setterList.contains(setter)) {
			return;
		}
		setterList.add(setter);
		parameterMap.putAll(setter.getPropertyMap());
	}
}
